﻿namespace Hardcodet.Wpf.TaskbarNotification.Interop
{
    using System;
    using System.ComponentModel;
    using System.Runtime.CompilerServices;
    using System.Threading;

    public class WindowMessageSink : IDisposable
    {
        public const int CallbackMessageId = 0x400;
        private bool isDoubleClick;
        private WindowProcedureHandler messageHandler;
        private uint taskbarRestartMessageId;

        public event Action<bool> BallonToolTipChanged;

        public event Action<bool> ChangeToolTipStateRequest;

        public event Action<MouseEvent> MouseEventReceived;

        public event Action TaskbarCreated;

        private WindowMessageSink()
        {
        }

        public WindowMessageSink(NotifyIconVersion version)
        {
            this.Version = version;
            this.CreateMessageWindow();
        }

        internal static WindowMessageSink CreateEmpty()
        {
            return new WindowMessageSink { MessageWindowHandle = IntPtr.Zero, Version = NotifyIconVersion.Vista };
        }

        private void CreateMessageWindow()
        {
            WindowClass class2;
            this.WindowId = "WPFTaskbarIcon_" + DateTime.Now.Ticks;
            this.messageHandler = new WindowProcedureHandler(this.OnWindowMessageReceived);
            class2.style = 0;
            class2.lpfnWndProc = this.messageHandler;
            class2.cbClsExtra = 0;
            class2.cbWndExtra = 0;
            class2.hInstance = IntPtr.Zero;
            class2.hIcon = IntPtr.Zero;
            class2.hCursor = IntPtr.Zero;
            class2.hbrBackground = IntPtr.Zero;
            class2.lpszMenuName = "";
            class2.lpszClassName = this.WindowId;
            WinApi.RegisterClass(ref class2);
            this.taskbarRestartMessageId = WinApi.RegisterWindowMessage("TaskbarCreated");
            this.MessageWindowHandle = WinApi.CreateWindowEx(0, this.WindowId, "", 0, 0, 0, 1, 1, 0, 0, 0, 0);
            if (this.MessageWindowHandle == IntPtr.Zero)
            {
                throw new Win32Exception();
            }
        }

        public void Dispose()
        {
            this.Dispose(true);
            GC.SuppressFinalize(this);
        }

        private void Dispose(bool disposing)
        {
            if (!this.IsDisposed && disposing)
            {
                this.IsDisposed = true;
                WinApi.DestroyWindow(this.MessageWindowHandle);
                this.messageHandler = null;
            }
        }

        ~WindowMessageSink()
        {
            this.Dispose(false);
        }

        private long OnWindowMessageReceived(IntPtr hwnd, uint messageId, uint wparam, uint lparam)
        {
            if (messageId == this.taskbarRestartMessageId)
            {
                this.TaskbarCreated();
            }
            this.ProcessWindowMessage(messageId, wparam, lparam);
            return WinApi.DefWindowProc(hwnd, messageId, wparam, lparam);
        }

        private void ProcessWindowMessage(uint msg, uint wParam, uint lParam)
        {
            if (msg == 0x400)
            {
                switch (lParam)
                {
                    case 0x200:
                        this.MouseEventReceived(MouseEvent.MouseMove);
                        return;

                    case 0x201:
                        this.MouseEventReceived(MouseEvent.IconLeftMouseDown);
                        return;

                    case 0x202:
                        if (!this.isDoubleClick)
                        {
                            this.MouseEventReceived(MouseEvent.IconLeftMouseUp);
                        }
                        this.isDoubleClick = false;
                        return;

                    case 0x203:
                        this.isDoubleClick = true;
                        this.MouseEventReceived(MouseEvent.IconDoubleClick);
                        return;

                    case 0x204:
                        this.MouseEventReceived(MouseEvent.IconRightMouseDown);
                        return;

                    case 0x205:
                        this.MouseEventReceived(MouseEvent.IconRightMouseUp);
                        return;

                    case 0x206:
                    case 0x209:
                        return;

                    case 0x207:
                        this.MouseEventReceived(MouseEvent.IconMiddleMouseDown);
                        return;

                    case 520:
                        this.MouseEventReceived(MouseEvent.IconMiddleMouseUp);
                        return;

                    case 0x402:
                        this.BallonToolTipChanged(true);
                        return;

                    case 0x403:
                    case 0x404:
                        this.BallonToolTipChanged(false);
                        return;

                    case 0x405:
                        this.MouseEventReceived(MouseEvent.BalloonToolTipClicked);
                        return;

                    case 0x406:
                        this.ChangeToolTipStateRequest(true);
                        return;

                    case 0x407:
                        this.ChangeToolTipStateRequest(false);
                        return;
                }
            }
        }

        public bool IsDisposed { get; private set; }

        internal IntPtr MessageWindowHandle { get; private set; }

        public NotifyIconVersion Version { get; set; }

        internal string WindowId { get; private set; }
    }
}

